#include "fxcc/core/graph/directX11/Effect3d.h"

using namespace fxcc::graph::directX11;

fxcc::graph::directX11::Effect3d::Effect3d(const std::string& fragMainCode)
    :m_FragMainCode(fragMainCode)
{
    m_PassBuffer = std::make_shared<UploadBuffer>(sizeof(PassData));
    m_ObjBuffer = std::make_shared<UploadBuffer>(sizeof(ObjData));
    m_BoneBuffer = std::make_shared<UploadBuffer>(sizeof(BoneData));

    std::string fixedVertCode = FixedVertCode();
    std::string fragCode = GetFragCode();

    m_Shader = std::make_shared<fxcc::graph::directX11::Shader>(fixedVertCode, fragCode, fxcc::graph::common::VertexType::_vertex_fixed_);
};


std::string fxcc::graph::directX11::Effect3d::GetFragCode()
{
    kainjow::mustache::mustache m1 = GetFragTemp();

    kainjow::mustache::data d1;
    d1.set("FragMain", this->m_FragMainCode);

    return m1.render(d1);
}

void fxcc::graph::directX11::Effect3d::SetObjData(const ObjData& objData)
{
    m_ObjBuffer->Upload<ObjData>(objData);
};


void fxcc::graph::directX11::Effect3d::SetCamera(const fxcc::graph::common::Camera& camera)
{
    PassData passData;
    passData.Load(camera);
    SetPassData(passData);
}

void fxcc::graph::directX11::Effect3d::SetPassData(const PassData& passData)
{
    m_PassBuffer->Upload<PassData>(passData);

}

void fxcc::graph::directX11::Effect3d::SetBoneData(const BoneData& boneData)
{
    m_PassBuffer->Upload<BoneData>(boneData);

}
void fxcc::graph::directX11::Effect3d::Begin()
{
    m_Shader->Use();
    m_PassBuffer->VSSet(0);
    m_ObjBuffer->VSSet(1);
    m_BoneBuffer->VSSet(2);

};


void fxcc::graph::directX11::Effect3d::InstMeshJack(const fxcc::graph::directX11::MeshJack* mesh)
{
    m_Shader->Use();

    mesh->Bind();
    mesh->DrawElementsAuto();

};

void fxcc::graph::directX11::Effect3d::InstSkinnedMeshJack(const fxcc::graph::directX11::MeshJack* mesh)
{
    m_AnimShader->Use();
    mesh->Bind();
    mesh->DrawElementsAuto();
}

void fxcc::graph::directX11::Effect3d::End()
{

}

std::string fxcc::graph::directX11::Effect3d::CommonCode()
{
    return std::string();
}

std::string fxcc::graph::directX11::Effect3d::GetFragTemp()
{
    return R"(
SamplerState m_Texture2dSampler : register(s0); 
SamplerState m_Texture2dArraySampler : register(s1); 
SamplerState m_TextureCubeSampler : register(s2); 

struct  PSInput
{
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD0;
};

{{{FragMain}}}

)";
}

std::string fxcc::graph::directX11::Effect3d::FixedVertCode()
{
	return R"(
struct VSInput
{
    float3 pos : POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOORD;
    float3 tangent : TANGENT;
    float3 bigTangent : BIGTANGENT;
};

struct  PSInput
{
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD0;
};




cbuffer PassDataBuffer : register(b0) 
{
	float4x4 m_Proj;
	float4x4 m_View;
	float4x4 m_ProjView;
	float3 m_ViewPos;
	float m_NearZ;
	float2 pad;
	int m_UseTessllation;
	float m_FarZ;
              
};

cbuffer ObjDataBuffer : register(b1) 
{
    float4x4 world;                  
    float4x4 worldInv;               
    float4x4 worldInvTranspose;      
	int invertNormal;
	float borderNormalAxis;
	int objId;
	float objBufferPad;
	float2 m_TexStart;
	float2 m_TexCoordSepNum;
         
};

PSInput main(VSInput input)
{
    PSInput output;
	float4 worldPosition = float4(input.pos, 1.0f);
    output.pos = mul(m_Proj, mul(m_View, mul(world, worldPosition))); 
  	output.tex = input.tex;
    return output;
}

)";
}

std::string fxcc::graph::directX11::Effect3d::SkinnedVertCode()
{
    return R"(

#define MAX_BONES_NUM 256
#define MAX_DIRECTION_NUM 8
#define MAX_POINT_NUM 8
#define MAX_SPOT_NUM 8


struct ObjData
{
	float4x4 m_World;
	float4x4 m_WorldInv;
	float4x4 m_WorldInvTranspose;
	int m_InvertNormal;
	float m_BorderNormalAxis;
	int m_ObjId;
	float m_ObjBufferPad;
	float2 m_TexStart;
	float2 m_TexCoordSepNum;
};

struct PassData
{
    float4x4 m_Proj;
    float4x4 m_View;
    float4x4 m_ProjView;
    float3 m_ViewPos;
    float m_NearZ;
    float2 m_Pad2 ;
    int m_UseTessllation;
    float m_FarZ;
};


struct BoneData
{
    float4x4 bones[MAX_BONES_NUM];
};

struct   DirLight {
    float3 ambient;
    int ambientMap;

    float3 diffuse;
    int diffuseMap;

    float3 specular;
    int specularMap;

    float3 direction;
    float pad;

};

struct   PointLight {
    float3 ambient;
    int ambientMap;

    float3 diffuse;
    int diffuseMap;

    float3 specular;
    int specularMap;

    float3 position;
    float pad3;

	float3 lightPbrColor;
	float pad2;

    float constant;
    float linear;
    float quadratic;
    float pad4;
};

struct   SpotLight {
    float3 ambient;
    int ambientMap;

    float3 diffuse;
    int diffuseMap;

    float3 specular;
    int specularMap;

    float3 position;
    float cutOff;
    float3 direction;
    float outerCutOff;
  
    float constant;
    float linear;
    float quadratic;
    float pad;
   
};


struct  LightEffect {

    DirLight dirLights[MAX_DIRECTION_NUM];
    PointLight pointLights[MAX_POINT_NUM];
    SpotLight spotLights[MAX_SPOT_NUM];

    int numDirs;
    int numPoints ;
    int numSpots;
    int pad;
};

struct VSInput
{
    float3 pos : POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOORD;
    float3 tangent : TANGENT;
    float3 bigTangent : BIGTANGENT;

};

struct PSInput
{
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD0;
};

cbuffer PassDataBuffer : register(b0) 
{
    PassData passData;
};

cbuffer ObjDataBuffer : register(b1) 
{
    ObjData objData;
};

cbuffer ObjDataBuffer : register(b2) 
{
    BoneData boneData;
};

PSInput main(VSInput input)
{
    
    PSInput output;
	float4 worldPosition = float4(input.pos, 1.0f);
    output.pos = mul(passData.m_Proj, mul(passData.m_View, mul(objData.world, worldPosition))); 
  	output.tex = input.tex;
    return output;
}
)";
}